package main.java.com.async.models;


import com.ptc.cipjava.jxthrowable;
import com.ptc.pfc.pfcAssembly.Assembly;
import com.ptc.pfc.pfcExceptions.XToolkitNotFound;
import com.ptc.pfc.pfcExceptions.XUnknownModelExtension;
import com.ptc.pfc.pfcFamily.FamilyTableRows;
import com.ptc.pfc.pfcModel.*;
import com.ptc.pfc.pfcSession.Session;
import com.ptc.pfc.pfcSolid.Solid;
import com.ptc.pfc.pfcWindow.Window;
import main.java.com.async.tools.AsyncWoodTool;
import main.java.com.async.tools.LogMessage;

import java.io.FileNotFoundException;
import java.util.HashSet;

public class AsyncFunction {

	private static String _info = ""; // Creo弹出通知内容

    /**
     * 构造函数
     */
    public AsyncFunction(){

    }

    /**
     * 打开模型
     * @param session 当前会话
     * @param params 字符串数组,依次包括模型所在文件夹路径,模型的文件名
     * @throws jxthrowable
     * @throws FileNotFoundException
     */
	public static void openModel(Session session, String[] params) throws jxthrowable, FileNotFoundException {
        String modelPath = params[0];
        String modelName = params[1];
//		备份当前工作目录
		String currentDir = session.GetCurrentDirectory();
		session.ChangeDirectory(modelPath);
		Model currentModel = null;
		try {
			ModelDescriptor modelDescriptor = pfcModel.ModelDescriptor_CreateFromFileName(modelName);
			currentModel = session.RetrieveModel(modelDescriptor);
		} catch (XToolkitNotFound | XUnknownModelExtension x) {
			// TODO: handle exception
			LogMessage.logError(x);
//			System.out.println("无法读取模型路径,请检查模型路径有效性。");
			return;
		}
//		prt文件可能存在问题,文件存在却无法获取模型本体
		if(currentModel != null){
			currentModel.Display();
		}
		Window window = session.GetCurrentWindow();
		window.Activate();
//		恢复当前工作目录
		session.ChangeDirectory(currentDir);
	}

    /**
     * 装配指定零件或者组件
     * @param session 当前会话
     * @param params 字符串数组,依次包括模型所在文件夹路径,以及模型的文件名
     * @throws jxthrowable
     * @throws FileNotFoundException
     */
	public static void assembleModel(Session session, String[] params) throws jxthrowable, FileNotFoundException {
        String modelPath = params[0];
        String currentModelFile = params[1];
//		备份当前工作目录
		String currentDir = session.GetCurrentDirectory();
//		获取当前窗口的装配模型
		Assembly assembly = (Assembly) session.GetCurrentModel();
//		将工作目录变更至需要装配的模型路径下
		session.ChangeDirectory(modelPath);
//		通过新模型名称尝试从session中获取该模型
		Model targetModel = session.GetModelFromFileName(currentModelFile);
		if(targetModel == null){
			ModelDescriptor modelDescriptor = pfcModel.ModelDescriptor_CreateFromFileName(currentModelFile);
			targetModel = session.RetrieveModel(modelDescriptor);
			assembly.AssembleComponent((Solid) targetModel, null);
		}
		else {
			assembly.AssembleComponent((Solid) targetModel, null);
		}
		Window window = session.GetCurrentWindow();
		window.Activate();
//		恢复当前工作目录
		session.ChangeDirectory(currentDir);
	}

	/**
	 *	按命名规则重命名当前模型
	 * @param session 当前会话
	 * @param params	字符串数组,依次包括前缀,后缀,特殊字符
	 * @throws jxthrowable
	 */
	public static void renameCurModel(Session session, String[] params) throws jxthrowable {
        String prefix = params[0];
        String postfix = params[1];
        String specialSymbol = params[2];
		AsyncWoodTool asyncWoodTool = new AsyncWoodTool(session);
		Model targetModel = session.GetCurrentModel();
//				若是零件
		if (targetModel.GetType() == ModelType.MDL_PART) {
			renamePart(targetModel, prefix, postfix, specialSymbol);
		}
//				若是装配件
		else if (targetModel.GetType() == ModelType.MDL_ASSEMBLY) {
//				对装配体所有文件进行重命名
			renameAsm(targetModel, prefix, postfix, specialSymbol);
		}
		if(_info.length() != 0){
			asyncWoodTool.pop(_info);
			_info = "";
		}
	}

	/**
	 *	用以检测模型名称长度是否合理，并返回新模型名称
	 * @param currentModelName 当前的模型名称
	 * @param prefix	前缀
	 * @param postfix	后缀
	 * @param specialSymbol 特殊字符
	 * @return 返回新模型名称,若新名称字符超长了,则返回null
	 */
	private static String getNewModelName(String currentModelName, String prefix, String postfix, String specialSymbol){
        String newModelName = currentModelName;
        Integer newModelNameLen = currentModelName.length();
//      有前缀就要通过下划线连接模型名称
	    if(prefix.length() != 0){
            newModelNameLen = prefix.length() + 1 + newModelNameLen;
            newModelName = prefix + "_" + newModelName;
        }
//	    有后缀就要通过下划线连接模型名称
	    if(postfix.length() != 0 ){
            newModelNameLen = newModelNameLen + 1 + specialSymbol.length() + postfix.length();
            newModelName = newModelName + "_" + specialSymbol + postfix;
        }
//		若新模型名称长度超过30则返回null
		if(newModelNameLen > 30){
			return null;
		}
		return newModelName;
	}

	/**
	 * 重命名组件的所有元素
	 * @param assemblyModel 组件模型对象
	 * @param prefix 前缀
	 * @param postfix 后缀
	 * @param specialSymbol 特殊字符
	 * @throws jxthrowable
	 */
	public static void renameAsm(Model assemblyModel, String prefix, String postfix, String specialSymbol) throws jxthrowable {
		HashSet<String> modelsName = new HashSet<>();
		Session session = (Session) assemblyModel.GetDBParent();
		Solid asmSolid = (Solid)assemblyModel;
		extractAsmAllFileName(session, asmSolid, modelsName);
		for(String fileName : modelsName){
//			这里需要注意,如果是被隐含的实体,是无法取到Model本体的,这时候Model为null
			Model model = session.GetModelFromFileName(fileName);
			if(model != null && model.GetType() == ModelType.MDL_ASSEMBLY){
				String newModelName = getNewModelName(model.GetInstanceName(), prefix, postfix, specialSymbol);
				if(newModelName != null){
					model.Rename(newModelName, null);
				}
				else {
					_info = _info + model.GetFileName() + "的新模型名超过30个字符,无法自动重新命名" + "\n\n";
				}

			}
			else if(model != null && model.GetType() == ModelType.MDL_PART){
				renamePart(model, prefix, postfix, specialSymbol);
			}
		}
	}

	/**
	 * 获取装配件的所有元素文件名称,包括隐含的元素以及族表元素
	 * @param session 当前会话
	 * @param asmSolid 装配件实体
	 * @param modelsName 用于记录所有文件名称
	 * @throws jxthrowable
	 */
	private static void extractAsmAllFileName(Session session, Solid asmSolid, HashSet<String> modelsName) throws jxthrowable {
//		如果组件是族表文件
		if(hasFamilyMemberModel(asmSolid)){
			if(asmSolid.GetGenericName() != null){
				asmSolid = (Solid) session.GetModel(asmSolid.GetGenericName(), asmSolid.GetType());
			}
//          获取组件的族表的每一个成员行
			FamilyTableRows familyTableRows = asmSolid.ListRows();
			modelsName.add(asmSolid.GetFileName());
			for(int i = 0; i < familyTableRows.getarraysize(); i++){
//		        将组件族表成员添加至内存并再生
				Solid asmFamMemSolid = (Solid)familyTableRows.get(i).CreateInstance();
				modelsName.add(asmFamMemSolid.GetFileName());
//				获取当前组件族表成员的所有依赖子组件包括被隐藏隐含的
				Dependencies dependencies = asmFamMemSolid.ListDependencies();
//				对该族表成员的所有子组件遍历,若是零件则记录名字,若是组件就进入递归
				for(int j = 0; j < dependencies.getarraysize(); j++){
					ModelDescriptor modelDescriptor = dependencies.get(j).GetDepModel();
					modelsName.add(modelDescriptor.GetFileName());
					if(modelDescriptor.GetType() == ModelType.MDL_ASSEMBLY){
						Model compoModel = session.RetrieveModel(modelDescriptor);
						extractAsmAllFileName(session, (Solid) compoModel, modelsName);
					}
				}
			}
		}
//      不是族表成员,也不包含族表
		else {
//			先保存组件的文件名
			modelsName.add(asmSolid.GetFileName());
//			获取组件所有的依赖子组件包括被隐藏隐含的
			Dependencies dependencies = asmSolid.ListDependencies();
//			对所有子组件遍历,若是零件则记录名字,若是组件就进入递归
			for(int j = 0; j < dependencies.getarraysize(); j++){
				ModelDescriptor modelDescriptor = dependencies.get(j).GetDepModel();
				modelsName.add(modelDescriptor.GetFileName());
				if(modelDescriptor.GetType() == ModelType.MDL_ASSEMBLY){
					Model compoModel = session.RetrieveModel(modelDescriptor);
					extractAsmAllFileName(session, (Solid) compoModel, modelsName);
				}
			}
		}
	}

	/**
	 * 重命名零件名称
	 * @param model 零件模型对象
	 * @param prefix 前缀
	 * @param postfix 后缀
	 * @param specialSymbol	特殊字符
	 * @throws jxthrowable
	 */
	private static void renamePart(Model model, String prefix, String postfix, String specialSymbol) throws jxthrowable {
	    Session session = (Session) model.GetDBParent();
		Solid solid = (Solid) model;
//		判断模型是否是族表成员或者包含族表
		if(hasFamilyMemberModel(model)){
			String genericName = model.GetGenericName();
//			若当前模型为父级,必然取不到genericName
			if(genericName == null){
                renamePrtFamilyMember(solid, prefix, postfix, specialSymbol);
			}
//			若当前模型为子级,可以取到genericName
			else{
				solid = (Solid) session.GetModel(genericName, model.GetType());
                renamePrtFamilyMember(solid, prefix, postfix, specialSymbol);
			}
		}
//		不包含族表文件的零件可以直接重命名
		else {
			String newModelName = getNewModelName(model.GetInstanceName(), prefix, postfix, specialSymbol);
			if(newModelName != null){
				model.Rename(newModelName, null);
			}
//			如果新模型名称不符合规则
			else{
				_info = _info + model.GetFileName() + "的新模型名超过30个字符,无法自动重新命名" + "\n\n";
            }
		}
	}

	/**
	 * 重命名族表成员的名称
	 * @param solid 父级模型的solid对象
	 * @param prefix 前缀
	 * @param postfix 后缀
	 * @param specialSymbol 特殊字符
	 * @throws jxthrowable
	 */
	private static void renamePrtFamilyMember(Solid solid, String prefix, String postfix, String specialSymbol) throws jxthrowable {
	    Session session = (Session) solid.GetDBParent();
		FamilyTableRows familyTableRows = solid.ListRows();
		for(int i = 0; i < familyTableRows.getarraysize(); i++){
//		    将族表成员添加至内存并再生
			Model familyMember = familyTableRows.get(i).CreateInstance();
			String newfamilyMemberName = getNewModelName(familyMember.GetInstanceName(), prefix, postfix, specialSymbol);
			if(newfamilyMemberName != null){
				familyMember.Rename(newfamilyMemberName, null);
			}
			else{
				_info = _info + familyMember.GetFileName() + "的新模型名超过30个字符,无法自动重新命名" + "\n\n";
			}
		}
//		最后对generic重命名
		String solidNewName = getNewModelName(solid.GetInstanceName(), prefix, postfix, specialSymbol);
		solid.Rename(solidNewName, null);
	}

	/**
	 * 判断读入的模型是否包含族表
	 * @param model	模型对象
	 * @return 包含族表就返回true,否则就返回false
	 * @throws jxthrowable
	 */
	private static Boolean hasFamilyMemberModel(Model model) throws jxthrowable {
		Solid currentSolid = (Solid)model;
		String genericName = model.GetGenericName();
		Integer rowsNum = currentSolid.ListRows().getarraysize();
//		如果既没有父级名称,同时族表行数为0,那么该模型就不包含族表
		if(genericName == null && rowsNum == 0){
			return false;
		}
		return true;
	}
}
